package com.stars.easyms.logger.asyn;

import com.stars.easyms.base.asynchronous.BaseAsynchronousTask;
import com.stars.easyms.logger.EasyMsLoggerHandler;
import com.stars.easyms.logger.config.EasyMsLoggerHelper;
import com.stars.easyms.logger.function.EasyMsLoggerExecutor;
import com.stars.easyms.logger.util.EasyMsLoggerExecutorHolder;
import org.slf4j.Logger;
import org.slf4j.Marker;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * <p>className: EasyMsAsynLogger</p>
 * <p>description: 异步打印日志的类</p>
 *
 * @author guoguifang
 * @version 1.3.0
 * @date 2019-08-15 19:28
 */
public final class EasyMsAsynLogger<E> extends BaseAsynchronousTask<E> {

    private static final EasyMsAsynLogger INSTANCE = new EasyMsAsynLogger();

    /**
     * 保证顺序执行的锁队列
     */
    private final Queue<Worker> lockQueue = new LinkedBlockingQueue<>();

    /**
     * 日志处理者
     */
    private final EasyMsLoggerHandler<E> easyMsLoggerHandler = EasyMsLoggerHelper.getLogFrameworkType().getEasyMsLoggerHandler();

    public void log(Logger logger, Marker marker, String levelStr, String msg, Object[] params, Throwable throwable) {
        E e = easyMsLoggerHandler.filterAndGetEvent(logger, marker, levelStr, msg, params, throwable);
        if (e != null) {
            add(e);
        }
    }

    @Override
    protected boolean execute(E e) {
        // 日志需要保证顺序执行，当批量处理时为了保证顺序执行因此增加锁队列
        final Worker currWorker = getCurrentWorker();
        lockQueue.offer(currWorker);

        // 将日志任务执行列表放入本地线程变量中
        List<EasyMsLoggerExecutor> easyMsLoggerExecutorList = new ArrayList<>();
        EasyMsLoggerExecutorHolder.setLoggerExecutorList(easyMsLoggerExecutorList);

        // 执行日志处理操作并清除本地线程变量中的日志任务执行列表
        try {
            easyMsLoggerHandler.append(e);
        } finally {
            EasyMsLoggerExecutorHolder.clear();
        }

        // 如果当前工作线程不在锁队列第一位则等待
        currWorker.await(() -> lockQueue.peek() != currWorker);

        // 如果当前工作线程在锁队列第一位时则执行日志任务执行操作
        if (!easyMsLoggerExecutorList.isEmpty()) {
            easyMsLoggerExecutorList.forEach(EasyMsLoggerExecutor::execute);
        }

        // 将该锁队列第一位去掉并唤醒下一个工作线程
        lockQueue.poll();
        Worker nextWorker = lockQueue.peek();
        if (nextWorker != null) {
            nextWorker.signal();
        }
        return true;
    }

    @Override
    protected int capacity() {
        return 10000000;
    }

    @Override
    protected int singleWorkerThreshold() {
        return 5000;
    }

    @Override
    protected int maxWorkerCount() {
        return 5;
    }

    public static EasyMsAsynLogger getInstance() {
        return INSTANCE;
    }

    private EasyMsAsynLogger() {
    }
}